package com.tiancheng.trade.payment.channel.apple;

/**
 * @Author: likailun
 * @Description:
 * @Date: create in 2022/5/30 3:48 下午
 */

import com.alibaba.fastjson.JSONObject;
import com.tiancheng.commom.pay.PayOrderAutoQueryConfig;
import com.tiancheng.trade.commom.core.data.Result;
import com.tiancheng.trade.commom.core.exception.BusinessException;
import com.tiancheng.trade.commom.core.utils.JsonUtil;
import com.tiancheng.trade.commom.web.http.PlatformRequest;
import com.tiancheng.trade.commom.core.utils.ServiceLocator;
import com.tiancheng.trade.payment.bo.cmq.ApplePayResultCheckBO;
import com.tiancheng.trade.commom.core.data.notify.PayResultMsgBO;
import com.tiancheng.trade.payment.bo.notify.PayNotifyBO;
import com.tiancheng.trade.payment.channel.AbstractPayChannel;
import com.tiancheng.trade.payment.channel.bo.*;
import com.tiancheng.trade.payment.configuration.AppleProperties;
import com.tiancheng.trade.payment.enums.AppleNotificationTypeEnum;
import com.tiancheng.trade.payment.enums.FlowStatusEnum;
import com.tiancheng.trade.commom.core.enums.PaymentChannelEnum;
import com.tiancheng.trade.payment.model.PaymentFlowDetailSettle;
import com.tiancheng.trade.payment.model.PaymentFlowDetailSettleDetail;
import com.tiancheng.trade.payment.service.PaymentOrderService;
import com.tiancheng.trade.payment.service.PaymentRefundService;
import com.tiancheng.trade.payment.mq.PaymentCenterMsgProducer;
import com.tiancheng.trade.payment.util.queue.RedisDelayQueueEnum;
import com.tiancheng.trade.payment.util.queue.RedisDelayQueueHandle;
import com.tiancheng.trade.payment.util.queue.RedisDelayQueueUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * apple 支付渠道
 */
@Slf4j
@Service
public class ApplePayChannel implements AbstractPayChannel,RedisDelayQueueHandle<ApplePayResultCheckBO> {

    @Resource
    private AppleProperties appleProperties;
    @Resource
    private PaymentCenterMsgProducer paymentCenterMsgProducer;
    @Resource
    private PaymentOrderService paymentOrderService;
    @Resource
    private PaymentRefundService paymentRefundService;
    @Resource
    private RedisDelayQueueUtil redisDelayQueueUtil;
    @Resource
    private PlatformRequest platformRequest;

    /**
     * 苹果支付结果查询
     *
     * @param applePayResultCheckBO 苹果支付参数
     * @return 支付结果
     */
    public Result unifiedOrder2Queue(ApplePayResultCheckBO applePayResultCheckBO) {
        final boolean isSc = redisDelayQueueUtil.addDelayQueue(applePayResultCheckBO, 1, TimeUnit.SECONDS,
                RedisDelayQueueEnum.TRADE_PAYMENT_APPLE_PAY_QUEUE.getCode());
        if (!isSc) {
            try {
                Thread.sleep(500);
                final boolean isScAgain = redisDelayQueueUtil.addDelayQueue(applePayResultCheckBO, 1, TimeUnit.SECONDS,
                        RedisDelayQueueEnum.TRADE_PAYMENT_APPLE_PAY_QUEUE.getCode());
                return isScAgain ? Result.success("苹果支付成功") : Result.fail("苹果支付失败");
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.error("重试消息苹果支付队列，线程暂停异常：{}", e.getMessage());
                return Result.fail("苹果支付失败");
            }
        }

        return Result.success();

    }

    /**
     * 苹果支付结果查询
     *
     * @param applePayResultCheckBO 苹果支付参数
     * @return 支付结果
     */
    public Result unifiedOrderV2(ApplePayResultCheckBO applePayResultCheckBO) {
        long startTime = System.currentTimeMillis();
        final Result result = this.applePayResultCheck(applePayResultCheckBO);
        long endTime = System.currentTimeMillis();
        log.info("验证苹果支付完成，耗时：【{} ms】，结果：{}",endTime - startTime, JsonUtil.toJsonString(result));
        return result;

    }

    /**
     * 调用苹果后台查询支付结果
     *
     * @param applePayResultCheckBO 查询参数
     * @return
     */
    public Result applePayResultCheck(ApplePayResultCheckBO applePayResultCheckBO) {

        //环境判断 线上/开发环境用不的请求链接
        String url = "";
        String appleEnv = "";
        final boolean isProd = ServiceLocator.getEnvironment().equals("prod");
        log.info("苹果支付内购，环境：{}", ServiceLocator.getEnvironment());
        if (isProd) {
            url = appleProperties.getUrlVerify(); //线上
        } else {
            url = appleProperties.getSandboxUrl(); //沙盒测试
        }
        final Map<String, Object> params = new HashMap<>();
        params.put("receipt-data", applePayResultCheckBO.getPayload());
        String verifyResult = this.appleHttpPost(url, params);
        if (StringUtils.isEmpty(verifyResult)) {
            log.error("验证失败，苹果返回数据空");
            return Result.fail("验证失败，苹果返回数据空");
        }
        log.info("线上，苹果平台返回JSON:" + verifyResult);
        JSONObject appleReturn = JSONObject.parseObject(verifyResult);
        String states = appleReturn.getString("status");
        //无数据则沙箱环境验证
        if ("21007".equals(states)) {
            verifyResult = this.appleHttpPost(appleProperties.getSandboxUrl(), params);
            log.info("沙盒环境，苹果平台返回JSON:" + verifyResult);
            appleReturn = JSONObject.parseObject(verifyResult);
            states = appleReturn.getString("status");
        }
        log.info("苹果平台返回值：appleReturn" + appleReturn);
        // 前端所提供的收据是有效的    验证成功
        if (states.equals("0")) {
            String receipt = appleReturn.getString("receipt");
            JSONObject returnJson = JSONObject.parseObject(receipt);
            String inApp = returnJson.getString("in_app");
            List<HashMap> inApps = JSONObject.parseArray(inApp, HashMap.class);
            if (!CollectionUtils.isEmpty(inApps)) {
                List<String> transactionIds = new ArrayList<String>();
                for (HashMap app : inApps) {
                    transactionIds.add((String) app.get("transaction_id"));
                }
                //交易列表包含当前交易，则认为交易成功
                if (transactionIds.contains(applePayResultCheckBO.getTransactionId())) {
                    //处理业务逻辑
                    log.info("苹果支付交易成功，处理订单：{}，flow_id:{}", applePayResultCheckBO.getTransactionId(), applePayResultCheckBO.getFlowId());
                    return Result.success(applePayResultCheckBO.getTransactionId());
                }
                return Result.fail("当前交易不在交易列表中");
            }
            log.error("未能获取获取到交易列表");
            return Result.fail("未能获取获取到交易列表");
        } else {
            log.error("apple支付失败，错误码：{}", states);
            return Result.fail("支付失败，错误码：" + states);
        }

    }

    private String appleHttpPost(String utl, Map<String, Object> params) {
        final Result<?> result = platformRequest.jsonPostRequest(utl, params, null);
        return JSONObject.toJSONString(result);
    }

    /**
     * 支付结果处理
     *
     * @param applePayResultCheckBO 支付要素
     * @param payStatus             支付状态
     */
    public void paySuccess(ApplePayResultCheckBO applePayResultCheckBO, FlowStatusEnum payStatus) {
        final PayResultBO payResultBO = new PayResultBO();
        payResultBO.setTransactionId(applePayResultCheckBO.getTransactionId());
        payResultBO.setTradeNo(applePayResultCheckBO.getTransactionId());
        payResultBO.setOrderId(applePayResultCheckBO.getFlowId());
        payResultBO.setAmount(applePayResultCheckBO.getAmount());
        payResultBO.setCouponAmount(0);
        payResultBO.setPayTime(LocalDateTime.now());// TODO 这里可以更换为苹果返回的支付时间
        payResultBO.setStatus(payStatus.getCode());
        final PayNotifyBO paymentOrder = paymentOrderService.payResult(payResultBO);

        // 发送消息通知订单中心
        PayResultMsgBO payResultMsgBO = new PayResultMsgBO();
        payResultMsgBO.setFlowId(paymentOrder.getFlowId());
        payResultMsgBO.setPaymentId(paymentOrder.getPaymentId());
        payResultMsgBO.setUserId(paymentOrder.getUserId());
        payResultMsgBO.setAmount(paymentOrder.getAmount());
        payResultMsgBO.setPayTime(paymentOrder.getPayTime());
        payResultMsgBO.setStatus(payResultBO.getStatus());
        payResultMsgBO.setTransactionId(paymentOrder.getTransactionId());
        payResultMsgBO.setTradeNo(paymentOrder.getTradeNo());
        payResultMsgBO.setOrderApp(paymentOrder.getOrderApp());
        payResultMsgBO.setPaymentChannel(paymentOrder.getPaymentChannel());
        payResultMsgBO.setPaymentWay(paymentOrder.getPaymentWay());
        payResultMsgBO.setPayScene(paymentOrder.getPayScene());
        payResultMsgBO.setCouponAmount(payResultBO.getCouponAmount());
        boolean success = paymentCenterMsgProducer.payResult(payResultMsgBO);
        if (!success) {
            log.warn("苹果支付消息推送失败，重试一次");
            paymentCenterMsgProducer.payResult(payResultMsgBO);
        }
    }

    @Override
    public void execute(ApplePayResultCheckBO bo) {
        if (null != bo) {
            final Result result = this.applePayResultCheck(bo);
            if (result.getRet() == 0) {
                this.paySuccess(bo, FlowStatusEnum.SUCCESS);
            } else {
                if (bo.getPosition() < 3) {
                    log.warn("校验苹果支付结果失败，重试第{}次", bo.getPosition() + 1);
                    this.unifiedOrder2Queue(new ApplePayResultCheckBO(bo.getTransactionId(), bo.getPayload(),
                            bo.getPosition() + 1, bo.getFlowId(), bo.getAmount()));
                } else {
                    log.warn("校验苹果支付结果失败，重试结束，支付失败");
                    this.paySuccess(bo, FlowStatusEnum.FAIL);
                }
            }
        }
    }

    @Override
    public PaymentChannelEnum getPaymentChannelEnum() {
        return PaymentChannelEnum.APPLE;
    }

    @Override
    public Result<UnifiedOrderResBO> unifiedOrder(UnifiedOrderBO unifiedOrderBO) throws BusinessException {
        this.unifiedOrder2Queue(new ApplePayResultCheckBO(unifiedOrderBO.getTransactionId(),
                unifiedOrderBO.getReceiptData(), 1, unifiedOrderBO.getFlowId(), unifiedOrderBO.getOrderAmount()));
        return Result.success(new UnifiedOrderResBO());
    }

    @Override
    public Result<GetUnifiedOrderResBO> get(GetUnifiedOrderBO getUnifiedOrderBO) throws BusinessException {
        return null;
    }

    @Override
    public Result<RefundOrderResBO> refund(RefundOrderBO refundOrderBO) throws BusinessException {
        log.info("{}退款，不需要调用渠道，直接返回成功！",this.getPaymentChannelEnum().getDesc());
        return Result.success(null);
    }

    @Override
    public Result<GetRefundOrderResBO> getRefund(GetRefundOrderBO getRefundOrderBO) throws BusinessException {
        return null;
    }

    @Override
    public Result close(CloseOrderBO closeOrderBO) throws BusinessException {
        return null;
    }

    @Override
    public PayResultBO payResult(Map<String, Object> payResult) {
        return null;
    }

    @Override
    public RefundResultBO refundResult(Map<String, Object> refundResult) {
        final AppleNotifications notifications = JsonUtil.fromJson(JsonUtil.toJsonString(refundResult), AppleNotifications.class);
        // 支付环境，Sandbox, PROD
        final String environment = notifications.getEnvironment();
        final String midTradeEnv = ServiceLocator.getEnvironment();
        log.info("苹果交易环境:{}，交易中台环境：{}", environment, midTradeEnv);
        if (environment.equalsIgnoreCase("sandbox") && midTradeEnv.equals("prod")) {
            // 苹果沙箱——交易中台生产，不处理
            log.error("苹果交易环境与交易中台支付环境不匹配，不处理");
            return null;
        }

        if (notifications.getNotificationType().equals(AppleNotificationTypeEnum.REFUND.getCode()) && null != notifications.getSignedTransactionInfo()) {
            return paymentRefundService.appleRefund(notifications.getSignedTransactionInfo(), AppleNotificationTypeEnum.REFUND);
        } else if (notifications.getNotificationType().equals(AppleNotificationTypeEnum.REFUND_DECLINED.getCode()) && null != notifications.getSignedTransactionInfo()) {
            return paymentRefundService.appleRefund(notifications.getSignedTransactionInfo(), AppleNotificationTypeEnum.REFUND_DECLINED);
        }
        log.error("苹果消息通知，未知的消息类型；{}",notifications.getNotificationType());
        return null;
    }

    @Override
    public Result settleV2(List<PaymentFlowDetailSettle> detailSettles, List<PaymentFlowDetailSettleDetail> detailSettlesDetails) throws BusinessException {
        return null;
    }

    @Override
    public boolean autoQueryStatus(Long orgPaymentChannelId) {
        return false;
    }

    @Override
    public PayOrderAutoQueryConfig getOrderAutoQueryConfig(Long orgPaymentChannelId) {
        return null;
    }
}
